package com.ecarx.vhaltests.hy11.testcases.drivemode;

import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DM_FUNC_DRIVE_MODE_SELECT;
import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DRIVE_MODE_SELECTION_DYNAMIC;
import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DRIVE_MODE_SELECTION_ECO;
import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DRIVE_MODE_SELECTION_NORMAL;
import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DRIVE_MODE_SELECTION_OFFROAD;
import static com.ecarx.xui.adaptapi.car.vehicle.IDriveMode.DRIVE_MODE_SPORT_PLUS;

import android.car.VehicleAreaType;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyManager;

import androidx.test.platform.app.InstrumentationRegistry;

import com.ecarx.vhaltests.IpcpClient;
import com.ecarx.vhaltests.VehicleConstants;
import com.ecarx.vhaltests.VhalUnitTestBase;
import com.ecarx.xui.adaptapi.car.IWrapper;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DmFuncDriveModeSelectTest extends VhalUnitTestBase {
    private static int propertyId;
    private static IWrapper.IPropertyId wrapperProperty;
    private CarPropertyManager.CarPropertyEventCallback propertyCbk;
    private static IpcpClient ipcpClient = new IpcpClient();
    ;
    private boolean isFirstCbk;
    private static final int AREA_ID = VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;

    private int cbkValue;
    private final Object cbkLock = new Object();

    private static int MODE_ECO;
    private static int MODE_NORMAL;
    private static int MODE_DYNAMIC;
    private static int MODE_SPORT_PLUS;
    private static int MODE_OFFROAD;

    @BeforeClass
    public static void testInit() {
        IWrapper wrapper = com.ecarx.xui.adaptapi.car.Car.createWrapper(InstrumentationRegistry.getInstrumentation().getTargetContext());
        wrapperProperty = wrapper.getWrappedPropertyId(IWrapper.WrappedIdType.ID_TYPE_FUNCTION, DM_FUNC_DRIVE_MODE_SELECT);
        propertyId = wrapperProperty.getPropertyId();
        MODE_ECO = wrapperProperty.getPropertyValue(DRIVE_MODE_SELECTION_ECO);
        MODE_NORMAL = wrapperProperty.getPropertyValue(DRIVE_MODE_SELECTION_NORMAL);
        MODE_DYNAMIC = wrapperProperty.getPropertyValue(DRIVE_MODE_SELECTION_DYNAMIC);
        MODE_SPORT_PLUS = wrapperProperty.getPropertyValue(DRIVE_MODE_SPORT_PLUS);
        MODE_OFFROAD = wrapperProperty.getPropertyValue(DRIVE_MODE_SELECTION_OFFROAD);
        setAvailable(true);
        Assert.assertTrue(isAvail());
    }

    @Test
    public void test001_SetDmFuncDriveModeSelectECO() {
        testSetGet(MODE_ECO);
    }

    @Test
    public void test002_SetDmFuncDriveModeSelectNormal() {
        testSetGet(MODE_NORMAL);
    }

    @Test
    public void test003_SetDmFuncDriveModeSelectDynamic() {
        testSetGet(MODE_DYNAMIC);
    }

    @Test
    public void test004_SetDmFuncDriveModeSelectSportPlus() {
        testSetGet(MODE_SPORT_PLUS);
    }

    @Test
    public void test005_SetDmFuncDriveModeSelectInvaildParam() {
        setValue(MODE_NORMAL);
        setValue(-1);
        Assert.assertEquals(MODE_NORMAL, getValue());
    }

    @Test
    public void test006_SetDmFuncDriveModeSelectChangeToDyno() {
        testSetGet(MODE_DYNAMIC);
        mockCarMode(VehicleConstants.CAR_MODE_DYNO);
        delay();
        Assert.assertEquals(MODE_NORMAL, getValue());
    }

    @Test
    public void test007_SetDmFuncDriveModeSelectCbk() {
        testCbk();
    }

    @Test
    public void test008_SetDmFuncDriveModeSelectUnAvail() {
        setAvailable(false);
        Assert.assertFalse(isAvail());
    }

    @Test
    public void test009_SetDmFuncDriveModeSelectOffroad() {
        testSetGet(MODE_OFFROAD);
    }

    private static void setAvailable(boolean isAvail) {
        int ccSvcId = 135;
        int[] ccOperIds = {200};

        int cemSvcId = 37;
        int[] cemOperIds = {999};

        ipcpClient.setConfig(ccSvcId, ccOperIds);
        ipcpClient.setConfig(cemSvcId, cemOperIds);

        int[] configs = {13, 220};
        int[] values = {4, 10};
        boolean ccRet = ipcpClient.setCarconfigSignal(ccSvcId, ccOperIds[0], configs, values);
        Assert.assertTrue(ccRet);

        String[] cems = {"vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts"};
        int[] cemValues = {isAvail ? VehicleConstants.CAR_MODE_NOMAL : VehicleConstants.CAR_MODE_TRANSPORT, VehicleConstants.USG_MODE_DRIVING};
        boolean cemRet = ipcpClient.setCEMSignal(cemSvcId, cemOperIds[0], cems, cemValues);
        Assert.assertTrue(cemRet);
    }

    private static void mockCarMode(int mode) {
        int cemSvcId = 37;
        int[] cemOperIds = {999};
        ipcpClient.setConfig(cemSvcId, cemOperIds);
        String[] cems = {"vehModMngtGlbSafe1CarModSts1", "vehModMngtGlbSafe1UsgModSts"};
        int[] cemValues = {mode, VehicleConstants.USG_MODE_DRIVING};
        boolean cemRet = ipcpClient.setCEMSignal(cemSvcId, cemOperIds[0], cems, cemValues);
        Assert.assertTrue(cemRet);
    }

    private void testSetGet(int value) {
        setValue(value);
        Assert.assertEquals(value, getValue());
    }

    private void setValue(int value) {
        setAvailable(true);
        delay();
        mCarPropertyMgr.setIntProperty(propertyId, AREA_ID, value);
    }

    private static int getValue() {
        delay();
        return mCarPropertyMgr.getIntProperty(propertyId, AREA_ID);
    }

    private static boolean isAvail() {
        delay();
        return mCarPropertyMgr.isPropertyAvailable(propertyId, AREA_ID);
    }

    private void testCbk() {
        cbkValue = -1;
        testSetGet(MODE_ECO);
        int expectedValue = MODE_SPORT_PLUS;
        propertyCbk = new CarPropertyManager.CarPropertyEventCallback() {
            @Override
            public void onChangeEvent(CarPropertyValue carPropertyValue) {
                if (carPropertyValue.getAreaId() == AREA_ID) {
                    if (isFirstCbk) {
                        isFirstCbk = false;
                    } else {
                        cbkValue = (int) carPropertyValue.getValue();
                        unLock();
                    }
                }
            }

            @Override
            public void onErrorEvent(int i, int i1) {
            }
        };
        mCarPropertyMgr.registerCallback(propertyCbk, propertyId, CarPropertyManager.SENSOR_RATE_ONCHANGE);
        delay();
        testSetGet(expectedValue);
        lock();
        Assert.assertEquals(expectedValue, cbkValue);
    }

    private void lock() {
        try {
            synchronized (cbkLock) {
                cbkLock.wait(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void unLock() {
        synchronized (cbkLock) {
            cbkLock.notify();
        }
    }

}